#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"

#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_mac.h"
#endif

#include "espnow.h"
#include "espnow_ctrl.h"
#include "espnow_storage.h"
#include "espnow_utils.h"
#include "esp_now_hal.h"
#include "driver/uart.h"
#include "driver/gpio.h"

#define CONFIG_RETRY_NUM 10

static esp_now_hal_data_t esp_now_pannel;                               /* define esp-now data pannel */
const espnow_addr_t ESPNOW_ADDR = {0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0XF1}; /* set local device mac address */
espnow_addr_t ESPNOW_BROADCAST_ADDR = {0x1A, 0xAA, 0xBB, 0xCC, 0xDD, 0XF0};

uint8_t primary = 0;
wifi_second_chan_t second = 0;

static const char *TAG = "esp_now_hal";

/**
 * @brief app_wifi_init
 *
 */
static void app_wifi_init()
{
    esp_event_loop_create_default();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_mac(ESP_IF_WIFI_STA, ESPNOW_ADDR));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MIN_MODEM)); /* save power */
    ESP_ERROR_CHECK(esp_wifi_start());
}

/**
 * @brief esp_now_data_pannel_init
 *
 * @return esp_err_t
 */
static esp_err_t esp_now_data_pannel_init(esp_now_hal_data_t *esp_now_hal_data)
{
    esp_now_hal_data->status = 1;
    esp_now_hal_data->esp_now_hal_living_room.light_switch = 1;
    esp_now_hal_data->esp_now_hal_living_room.fan_switch = 1;
    esp_now_hal_data->esp_now_hal_living_room.air_C = 1;
    esp_now_hal_data->esp_now_hal_living_room.smart_TV = 1;
    esp_now_hal_data->esp_now_hal_living_room.air_P = 1;
    esp_now_hal_data->esp_now_hal_bed_room.light_switch = 1;
    esp_now_hal_data->esp_now_hal_bed_room.fan_switch = 1;
    esp_now_hal_data->esp_now_hal_bed_room.air_C = 1;

    return ESP_OK;
}

/**
 * @brief esp_now_update_switch_data
 *
 */
static void esp_now_update_switch_data(esp_now_hal_data_t *esp_now_hal_data)
{

#if ESP_NOW_HAL_DEBUG
    ESP_LOGI(TAG, "esp-now recv data[update switch func]:%s", esp_now_hal_data->esp_now_recv_data);
#endif

    /* update esp-now recv status start */
    esp_now_hal_data->status = 0; /* has recv */
    /* update esp-now recv status end */

    /* update living room switch data start */
    if (esp_now_hal_data->esp_now_recv_data[0] != '0')
    {
        esp_now_hal_data->esp_now_hal_living_room.light_switch = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_living_room.light_switch = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[1] != '0')
    {
        esp_now_hal_data->esp_now_hal_living_room.fan_switch = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_living_room.fan_switch = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[2] != '0')
    {
        esp_now_hal_data->esp_now_hal_living_room.air_C = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_living_room.air_C = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[3] != '0')
    {
        esp_now_hal_data->esp_now_hal_living_room.smart_TV = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_living_room.smart_TV = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[4] != '0')
    {
        esp_now_hal_data->esp_now_hal_living_room.air_P = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_living_room.air_P = 0;
    }
    /* update living room switch data end */

    /* update bed room switch data start */
    if (esp_now_hal_data->esp_now_recv_data[5] != '0')
    {
        esp_now_hal_data->esp_now_hal_bed_room.light_switch = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_bed_room.light_switch = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[6] != '0')
    {
        esp_now_hal_data->esp_now_hal_bed_room.fan_switch = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_bed_room.fan_switch = 0;
    }
    if (esp_now_hal_data->esp_now_recv_data[7] != '0')
    {
        esp_now_hal_data->esp_now_hal_bed_room.air_C = 1;
    }
    else
    {
        esp_now_hal_data->esp_now_hal_bed_room.air_C = 0;
    }
    /* update bed room switch data end */
}

/**
 * @brief esp_now_recv_handle
 *
 * @return esp_err_t
 */
static esp_err_t esp_now_recv_handle(uint8_t *src_addr, void *data, size_t size, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
    ESP_PARAM_CHECK(src_addr);
    ESP_PARAM_CHECK(data);
    ESP_PARAM_CHECK(size);
    ESP_PARAM_CHECK(rx_ctrl);

    static uint32_t count = 0;

    char *esp_now_data_buff = (char *)data;
    ESP_LOGI(TAG, "esp_now_data_buff: %s", esp_now_data_buff);

    /* handle esp-now recv data start */
    esp_now_pannel.esp_now_recv_data = esp_now_data_buff;
    ESP_LOGI(TAG, "espnow_recv, <%" PRIu32 "> [" MACSTR "][%d][%d][%u]: %.*s",
             count++, MAC2STR(src_addr), rx_ctrl->channel, rx_ctrl->rssi, size, size, esp_now_pannel.esp_now_recv_data);
    esp_now_update_switch_data(&esp_now_pannel);
    /* handle esp-now recv data start */

    return ESP_OK;
}

/**
 * @brief esp_now_set_broadcast_addr
 *
 * @param addr_param
 */
void esp_now_set_broadcast_addr(uint8_t addr_param[6])
{
    for (uint8_t i = 0; i < 6; i++)
    {
        ESPNOW_BROADCAST_ADDR[i] = addr_param[i];
    }
}

uint8_t *get_espnow_broadcast_addr(void)
{
    return ESPNOW_BROADCAST_ADDR;
}

/**
 * @brief esp_now_get_switch_status
 *
 * @return bool
 */
bool esp_now_get_switch_status(esp_now_switch_data_type data_t)
{
    switch (data_t)
    {
    case RECV_STATUS:
        return esp_now_pannel.status;
    case LIVING_ROOM_LIGHT:
        return esp_now_pannel.esp_now_hal_living_room.light_switch;
    case LIVING_ROOM_FAN:
        return esp_now_pannel.esp_now_hal_living_room.fan_switch;
    case LIVING_ROOM_AIR_C:
        return esp_now_pannel.esp_now_hal_living_room.air_C;
    case LIVING_ROOM_SMART_TV:
        return esp_now_pannel.esp_now_hal_living_room.smart_TV;
    case LIVING_ROOM_AIR_P:
        return esp_now_pannel.esp_now_hal_living_room.air_P;
    case BED_ROOM_LIGHT:
        return esp_now_pannel.esp_now_hal_bed_room.light_switch;
    case BED_ROOM_FAN:
        return esp_now_pannel.esp_now_hal_bed_room.fan_switch;
    case BED_ROOM_AIRC:
        return esp_now_pannel.esp_now_hal_bed_room.air_C;
    default:
        return ESP_OK;
    }
}

/**
 * @brief esp_now_clear_rec_status
 *
 */
void esp_now_clear_rec_status(void)
{
    esp_now_pannel.status = 1; /* clear status */
}

/**
 * @brief esp_now_send_data
 *
 * @param esp_now_data
 */
void esp_now_send_data(char *esp_now_data)
{
    esp_err_t ret;

    espnow_frame_head_t frame_head = {
        .retransmit_count = CONFIG_RETRY_NUM,
        .broadcast = true,
    };
    ret = espnow_send(ESPNOW_DATA_TYPE_DATA, ESPNOW_BROADCAST_ADDR, esp_now_data, strlen(esp_now_data), &frame_head, portMAX_DELAY);
    if (ret != ESP_OK)
    {
        ESP_LOGE(TAG, "espnow send err.[%d]", ret);
    }
    else
    {
        ESP_LOGI(TAG, "espnow send successfully.[%d].data:%s.size:%d", ret, esp_now_data, strlen(esp_now_data));
    }
}

/**
 * @brief esp_now_wifi_init
 *
 */
void esp_now_wifi_init(void)
{
    espnow_config_t espnow_config = ESPNOW_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_now_data_pannel_init(&esp_now_pannel));
    ESP_ERROR_CHECK(espnow_storage_init());
    app_wifi_init();
    ESP_ERROR_CHECK(espnow_init(&espnow_config));
    ESP_ERROR_CHECK(espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_DATA, true, esp_now_recv_handle));
}

/**
 * @brief esp_now_get_channel
 *
 * @return uint8_t
 */
uint8_t esp_now_get_channel(void)
{
    return primary;
}

void esp_now_task(void *arg)
{
    esp_now_wifi_init();
    esp_log_level_set("espnow", ESP_LOG_NONE);      /* close esp-now warn log */
    esp_log_level_set("espnow_ctrl", ESP_LOG_NONE); /* close espnow_ctrl warn log */
    while (1)
    {
        esp_wifi_get_channel(&primary, &second);
#if ESP_NOW_HAL_DEBUG
        ESP_LOGW(TAG, "esp-now channel:%d  second:%d", primary, second);
#endif
        // ESP_LOGI(TAG, "initiator send press");
        // espnow_ctrl_initiator_send(ESPNOW_ATTRIBUTE_KEY_1, ESPNOW_ATTRIBUTE_POWER, data++);
        // esp_now_send_data("[3]:0");

        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
